/* 
 * Copyright (c) 2016, S.F. Express Inc. All rights reserved.
 */
package com.sf.mk.common.utils;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.LongAdder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 描述：
 * 
 * <pre>
 * HISTORY
 * ****************************************************************************
 *  ID   DATE           PERSON          REASON
 *  1    2016年2月20日     Robert          Create
 * ****************************************************************************
 * </pre>
 * 
 * @author Robert
 * @since 1.0
 */
public abstract class PerfTask<T> implements Callable<T> {
 
	private static final Logger log = LoggerFactory.getLogger(PerfTask.class);

	public static <T> long perf(int threadPool, List<Callable<T>> tasks, boolean printErrorMsg) throws Exception {

		long startTime = System.currentTimeMillis();

		ExecutorService es = Executors.newFixedThreadPool(threadPool);
		List<Future<T>> futures = es.invokeAll(tasks);

		LongAdder success = new LongAdder();
		LongAdder failed = new LongAdder();
		LongAdder done = new LongAdder();
		boolean exit = false;

		while (!exit) {
			for (Future<T> result : futures) {
				if (result.isDone()) {
					done.increment();

					try {
						T obj = result.get();
						if (obj instanceof Boolean) {
							if ((Boolean) obj)
								success.increment();
							else
								failed.increment();
						}
					} catch (ExecutionException e) {
						failed.increment();
						if (printErrorMsg) {
							log.warn("Failed to get result......", e);
						} else {
							log.warn(String.format("%s,Failed to get result......", e.getClass().getSimpleName()));
						}
					}
				}
			}
			if (done.longValue() >= (tasks.size() - 1)) {
				exit = true;
			}
		}
		long time = System.currentTimeMillis() - startTime;
		long count = done.longValue();

		double tps = count // * 2
				* new BigDecimal((double) 1000 / time).setScale(10, BigDecimal.ROUND_HALF_UP).doubleValue();

		String info = String.format("%s: Time:%s(ms), Done:%s(ops), TPS:%s(S), Success(result is boolean):%s,Failed:%s",
				tasks.get(0).getClass().getSimpleName(), time, count, (long) tps, success.longValue(),
				failed.longValue());
		System.out.println(info);
		log.info(info);
		es.shutdownNow();

		return failed.longValue();
	}

	public static <T> long perf(int threadPool, List<Callable<T>> tasks) throws Exception {
		return perf(threadPool, tasks, true);
	}

}
